/**
 * 
 */
package com.googlecode.iant.project;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.googlecode.iant.dom.IXmlElement;
import com.googlecode.iant.internal.dom.XmlElement;
import com.googlecode.iant.internal.project.Dependency;
import com.googlecode.iant.internal.project.Folder;
import com.googlecode.iant.internal.project.ProjectStructure;
import com.googlecode.iant.internal.project.IAntProject;



/**
 * @author ccPascu
 *
 */
public final class ProjectParser {
	
	/**
	 * 
	 */
	private static final ProjectParser instance = new ProjectParser();
	
	/**
	 * 
	 */
	private ProjectParser() {
	}
	

	public static ProjectParser getInstance(){
		return instance;
	}

	/**
	 * @param source
	 * @return the object whose definiion is contained inside that object
	 */
	public IProject parseProject(Resource source){
		final Element documentElement = parse(source);
		IXmlElement element = new XmlElement(documentElement);
		
		final IAntProject project = new IAntProject(source);
		
		/*
		 * Project dependencies
		 */
		final List<IXmlElement> dependenciesList = element.element("dependencies").list("dependency");
		for (IXmlElement dependencyElement : dependenciesList) {
			parseDependency(project, dependencyElement);
		}
		
		/*
		 * Project Structure
		 */
		try {
			final File file = source.getFile();
			final IProjectStructure projectStructure = new ProjectStructure(new FileSystemResource(file.getParentFile()));
			final IXmlElement structureElement = element.element("structure");
			parserProjectStructure(projectStructure, structureElement);
			project.setStructure(projectStructure);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return project;
	}


	private void parserProjectStructure(IProjectStructure projectStructure, IXmlElement structureElement) {
		if (!structureElement.isDefined()) {
			return;
		}
		
		final List<IXmlElement> sourceFolderElements = structureElement.element("sources").list("folder");
		for (IXmlElement folderElement : sourceFolderElements) {
			final IFolder folder = parseFolder(folderElement);
			projectStructure.addSourceFolder(folder);
		}
		
		final List<IXmlElement> testFolderElements = structureElement.element("tests").list("folder");
		for (IXmlElement folderElement : testFolderElements) {
			final IFolder folder = parseFolder(folderElement);
			projectStructure.addTestFolder(folder);
		}
	}


	private IFolder parseFolder(IXmlElement folderElement) {
		final String name = folderElement.attribute("name", true).value();
		final String include = folderElement.attribute("include", false).value();
		final String exclude = folderElement.attribute("exclude", false).value();
		return new Folder(name, include, exclude);
	}


	/**
	 * @param project
	 * @param dependencyElement
	 */
	private void parseDependency(final IAntProject project, IXmlElement dependencyElement) {
		final String group = dependencyElement.attribute("group", true).value();
		String artifact = dependencyElement.attribute("artifact", false).value();
		String groupVersion = dependencyElement.attribute("group", false).value();
		
		final boolean artifactIsDefined = StringUtils.hasText(artifact);
		if ( artifactIsDefined ) {
			project.addDependency(new Dependency(group, artifact, groupVersion));
		} else {
			final List<IXmlElement> artifactElements = dependencyElement.list("artifact");
			for (IXmlElement artifactElement : artifactElements) {
				artifact = artifactElement.attribute("name", true).value();
				String version = artifactElement.attribute("version", false).value();
				if (!StringUtils.hasText(version)) {
					version = groupVersion;
				}
				project.addDependency(new Dependency(group, artifact, version));
			}
		}
	}

	/**
	 * @param inputStream
	 * @return
	 */
	private Element parse(Resource source) {
		try {
			InputStream inputStream = source.getInputStream();
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
			final DocumentBuilder domBuilder = factory.newDocumentBuilder();
			final Document document = domBuilder.parse(inputStream);
			final Element documentElement = document.getDocumentElement();
			return documentElement;
		} catch (ParserConfigurationException e) {
			throw new RuntimeException(e);
		} catch (SAXException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
}
